package org.arl.modem.linktuner;

import jade.util.Logger;
import java.lang.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

public class ExperimentalBandits {
									//	MTYPE     DMODE	  MPSK      NC      NP      NZ    PKTLEN   FEC  
	public final static int brigands[][]={{1,		1,		1,		4,		6,		0,		1,		3},    //Coherent,freq,QPSK,Nc=1024,Np=256,Nz(=5),Pktlen=48,full coding. duration=188
										  {1,		1,		1,		4,		6,		0,		1,		2},    //Changed to only golay coding.                                   duration=117
										  {1,		1,		1,		4,		6,		0,		1,		1},    //Changed to only convolutional coding.                           duration=117
										  
										  {1,		1,		1,		3,		6,		0,		1,		3},    //Changed to Nc=512, full coding							   	     duration=174
										  {1,		1,		1,		3,		6,		0,		1,		2},    //Changed to only Golay coding                                    duration=88
										  {1,		1,		1,		3,		6,		0,		1,		1},    //Changed to only convolutional coding                            duration=131
										  
										  {1,		0,		0,		0,		6,		0,		1,		3},    //Coherent,time,BPSK,Nc=64,Np=256,Nz=0,Pktlen=48,full coding      duration=750
										  {1,		0,		0,		0,		6,		0,		1,		2},    //Changed to only Golay coding                                    duration=298
										  {1,		0,		0,		0,		6,		0,		1,		1},    //Changed to only convolutional coding                            duration=425
										  	
										  {1,		0,		0,		1,		6,		0,		1,		3},    //changed to Nc=128, full coding                                  duration=456
										  {1,		0,		0,		1,		6,		0,		1,		2},    //Changed to only Golay coding                                    duration=197
										  {1,		0,		0,		1,		6,		0,		1,		1},    //Changed to only Convolutional coding                            duration=283
										  
										  {0,		0,		0,		2,		6,		0,		1,		3},     //Incoherent,time,BPSK,Nc=256,Np=256,Nz=0,Pktlen=48,full coding  duration=1136
										  
										  //Schemes designed for failiure
            						//	MTYPE     DMODE	  MPSK      NC      NP      NZ    PKTLEN   FEC  

										  {1,		1,		1,		0,		0,		0,		1,		2},      //Coherent,freq,QPSK,Nc=64,Np=0,Nz=0,Pktlen=48,only Golay        duration=72
										  {1,		1,		1,		0,		0,		0,		1,		1},      //Changed to only Convolutional						          duration=84
										  {1,		1,		1,		1,		0,		0,		1,		2},      //Changed to Nc=128,only Golay                                   duration=75
										  {1,		1,		1,		1,		0,		0,		1,		1}      //Changed to only convolutional                                  duration=82
										  
										  //High performance schemes: for a channel that has high time coherence (high Nc makes the symbols longer).
								  //	MTYPE     DMODE	  MPSK      NC      NP      NZ    PKTLEN   FEC  
//										  {1,		1,		1,		4,		6,		0,		10,		3}, 	 //Coherent,freq,QPSK,Nc=1024,Np=256,Nz(=5),Pktlen=264,full coding.duration=474  pktlen=264               
//										  {1,		1,		1,		4,		6,		0,		32,		2},      //Changed to only Golay coding.                                   duration=474  pktlen=792
//										  {1,		1,		1,		4,		6,		0,		20,		1},      //Changed to only Convolutional coding.                           duration=474  pktlen=504
//										  
//										  {1,		1,		1,		3,		6,		0,		9,		3},      //Changed to Nc=512, full coding								   duration=518  pktlen=240
//										  {1,		1,		1,		3,		6,		0,		29,		2},		 //Changed to only Golay coding									   duration=518  pktlen=720
//										  {1,		1,		1,		3,		6,		0,		2,		1},		 //Changed to only Convolutional coding							   duration=518  pktlen=456
//										  
//										  //For channels that have low time coherence but high frequency coherence
//										  {1,		0,		0,		0,		3,		0,		3,		3},      //Coherent,time,BPSK,Nc=64,Np=32,Nz=0,Pktlen=96,full coding       duration=472  pktlen=96
//										  {1,		0,		0,		0,		3,		0,		12,		2},      //Changed to only Golay coding.								   duration=511  pktlen=312
//										  {1,		0,		0,		0,		3,		0,		7,		1},      //Changed to only Convolutional coding.						   duration=494  pktlen=192
//										  
//										  {1,		0,		0,		1,		4,		0,		3,		3},      //Coherent,time,BPSK,Nc=128,Np=64,Nz=0,Pktlen=,full coding        duration=450  pktlen=96
//										  {1,		0,		0,		1,		4,		0,		13,		2},      //Changed to only Golay coding                                    duration=516  pktlen=336
//										  {1,		0,		0,		1,		4,		0,		8,		1},      //Changed to only Convolutional coding                            duration=527  pktlen=216
										  
										  
										  
										  //At this point, i drop the Np to 64, 'coz otherwise the pkt_duration is much higher than 500ms. 
										  //{1,		0,		0,		0,		4,		0,		2,		3},    //Coherent,time,BPSK,Nc=64,Np=256,Nz=0,Pktlen=192,full coding       duration=

										  };
	
	//check if these are valid
	public final static double relativeDataRates[]={1.39899,   1.34199,   1.38303,   1.35033,   0.33333,   1.00000,   0.63636,   0.27729,
													0.83187,   0.52685,   0.12173,   0.36542,   0.23261,   0.12768,   0.38971,   0.24530,
													0.83182};
	
	public static boolean EXPERIMENT_COMPLETED=false;
	
	private static int banditID=0;
	public static int bandit[]=brigands[banditID];
	private static int banditCurrent;
	private static int banditPrev;
	private static int playCount=0;

	private static SmartBoy smart_boy;
	private static Logger log;
	private static ParamSetter param_setter;
	private static Exploit exploit;
	private static BanditIndices bandit_indices=new BanditIndices();
	protected static BanditParams[] bandit_params=new BanditParams[brigands.length];
	
	public final static int INITIATE_EXPERIMENTS=999;
//	public final static int SET_SCHEME_REQUEST_COMPLETED=1000;
	public final static int TRIGGER_TEST_PKT_TRAINS=1001;
	public static final int UPDATE_RECENT_STATS=1002;
	
	
	protected ExperimentalBandits(SmartBoy _smart_boy, ParamSetter _param_setter, Exploit _exploit, Logger _log){
		this.param_setter=_param_setter;
		this.smart_boy=_smart_boy;
		this.exploit=_exploit;
		this.log=_log;
		if(brigands.length!=relativeDataRates.length){
			System.out.println("lengths of brigands and relativeDataRates don't match");
		}
		for (int i = 0; i < brigands.length; i++) {
			bandit_params[i]=new BanditParams();
			bandit_params[i].normalizedIndex=bandit_params[i].gittinsIndex*relativeDataRates[i];
			System.out.println("bandit_params["+i+"].normalizedIndex = "+bandit_params[i].normalizedIndex);
		}
		System.out.println("max value = "+findMaxValueIndex(bandit_params)[0]+" , max index = "+findMaxValueIndex(bandit_params)[1]+"\n");
		banditID=(int)findMaxValueIndex(bandit_params)[1];
		banditCurrent=banditID;
		System.out.println("Experimental bandits initialized");
	}
	
	
	public static void runExperiment(int msgType){
		switch (msgType) {
		case INITIATE_EXPERIMENTS:
			//log.fine("in runExperiment : INITIATE_EXPERIMENTS");
			banditID=(int)findMaxValueIndex(bandit_params)[1];
			banditCurrent=banditID;
			if (playCount==0) {
				param_setter.currentSetSchemeParam=brigands[banditID];
				//crashes when you set param_setter.currentSetSchemeParam=brigands[2]. investigate why. perhaps its an invalid scheme???
				param_setter.setSchemeRequest(param_setter.currentSetSchemeParam, true);
				banditPrev=banditCurrent;
				log.fine("banditID = "+banditID+", index = "+findMaxValueIndex(bandit_params)[0]);
				break;
			}else if (banditCurrent!=banditPrev) {
				param_setter.currentSetSchemeParam=brigands[banditID];
				//crashes when you set param_setter.currentSetSchemeParam=brigands[2]. investigate why. perhaps its an invalid scheme???
				param_setter.setSchemeRequest(param_setter.currentSetSchemeParam, true);
				banditPrev=banditCurrent;
				log.fine("banditID = "+banditID+", index = "+findMaxValueIndex(bandit_params)[0]+" playCount = "+playCount);				
			}else {
				//current recommendation is same as previous, so we can proceed straight to play
				log.fine("banditID = "+banditID+", index = "+findMaxValueIndex(bandit_params)[0]+" playCount = "+playCount);
				log.fine("calling runExperiment(TRIGGER_TEST_PKT_TRAINS) ");
				runExperiment(TRIGGER_TEST_PKT_TRAINS);
			}
			break;

//		case SET_SCHEME_REQUEST_COMPLETED:
//			log.fine("about to send request to enable exploitation");
//			exploit.enableExploitationSetRequest(1, true);
//			break;
		case TRIGGER_TEST_PKT_TRAINS:
			//log.fine("in runExperiment : TRIGGER_TEST_PKT_TRAINS");
			exploit.ENABLE=1;
			log.fine("set exploit.ENABLE = 1");
			log.fine("about to call sendTestPacketTrain()");
			exploit.sendTestPacketTrain(2, 1, true);
			break;
		case UPDATE_RECENT_STATS:
			//log.fine("in runExperiment : UPDATE_RECENT_STATS");
			playCount++;
			//implicit assumption that each play consists of sending a single testpacket 
			if (exploit.getStats(exploit.GET_RECENT_ERROR_CNT)>0) {
				bandit_params[banditID].beta++;
			}else if(exploit.getStats(exploit.GET_RECENT_RX_TESTPKT_CNT)>0){ //need to check this condition 'coz sometimes u may get RxTestPktCnt=0
				bandit_params[banditID].alpha++;
			}
			//bandit_params[banditID].beta = bandit_params[banditID].beta + exploit.getStats(exploit.GET_RECENT_ERROR_CNT);
			//bandit_params[banditID].alpha = bandit_params[banditID].alpha +
			//		(param_setter.getPhyParam(param_setter.ACTIVE_SCHEME, param_setter.PKTLEN)*8) - exploit.getStats(exploit.GET_RECENT_ERROR_CNT);
			bandit_params[banditID].gittinsIndex=bandit_indices.gittinsIndex(bandit_params[banditID].alpha, bandit_params[banditID].beta, 0.75);
			bandit_params[banditID].normalizedIndex=bandit_params[banditID].gittinsIndex*relativeDataRates[banditID];
			log.fine("bandit_params["+banditID+"].alpha = " + bandit_params[banditID].alpha +
					" bandit_params["+banditID+"].beta = " + bandit_params[banditID].beta);
			log.fine("bandit_params["+banditID+"].gittinsIndex = " + bandit_params[banditID].gittinsIndex +
					" bandit_params["+banditID+"].normalizedIndex = " + bandit_params[banditID].normalizedIndex);

			if (!EXPERIMENT_COMPLETED) {
				log.fine("calling runExperiment(INITIATE_EXPERIMENTS) ");
				runExperiment(INITIATE_EXPERIMENTS);				
			}else {
				log.fine("end of experiment!");
				break;
			}
			break;
			//exploit.getStats(exploit.GET_RECENT_RX_TESTPKT_CNT);
		default:
			break;
		}
	}
	
	protected static double[] findMaxValueIndex(BanditParams[] bandit_params){
		double[] maxValueIndex={0,0};
		for (int i = 0; i < bandit_params.length; i++) {
			if(bandit_params[i].normalizedIndex>maxValueIndex[0]){
				maxValueIndex[0]=bandit_params[i].normalizedIndex;
				maxValueIndex[1]=i;
			}
		}
		return maxValueIndex;
	}
		
	
}
